﻿#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Media;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.Diagnostics;
using System.IO;
#endregion using

namespace Turn_ament
{
    abstract class Unit:ImageObject
    {
        #region Attributes
        protected string name;
        protected int health, damage, range, hitChance, hit;
        protected float maxMovement, movementLeft;
        protected bool isActive, moving;

        public enum Phase { Move, Attack, Done }
        protected Phase phase;
        protected Unit unitToAttack;
        protected Tile tile, tileBeforeMoving;
        protected List<Tile> path;
        protected Player player;

        protected Random random;
        #endregion Attributes

        #region Constructor
        public Unit(int health, int damage, int movement, int attackRange, int chanceToHit, Texture2D texture, Tile tile, int size, SpriteBatch spriteBatch)
            : base(texture, 0, 0, size, size, spriteBatch)
        {
            name = "-";
            random = new Random();
            this.health = health;
            this.damage = damage;
            maxMovement = movementLeft = movement;
            range = attackRange;
            HitChance = chanceToHit;
            Tile = tile;

            isActive = true; moving = false;
            Center = tile.Center;
            CenterOrigin();

            Refresh();
        }
        #endregion Constructor

        #region Properties
        public bool IsActive
        {
            get { return isActive; }

            set { isActive = value; }
        }

        public string Name
        {
            get { return name; }
        }

        public bool Moving
        {
            get { return moving; }

            set { moving = value; }
        }

        public Phase CurrentPhase
        {
            get { return phase; }

            set { phase = value; }
        }

        public bool PhaseDone
        {
            get { if (phase == Phase.Done) { return true; } else { return false; } }
        }

        public int Health
        {
            get { return health; }

            set { health = value; }
        }

        public int Damage
        {
            get { return damage; }

            set { damage = value; }
        }

        public float MovementLeft
        {
            get { return movementLeft; }

            set { movementLeft = value; }
        }

        public float MaxMovement
        {
            get { return maxMovement; }

            set { maxMovement = value; }
        }

        public int Range
        {
            get { return range; }

            set { range = value; }
        }

        public int HitChance
        {
            get { return hitChance; }

            set 
            { 
                hitChance = value;
                if (hitChance < 0)
                    hitChance = 0;
                else if (hitChance > 100)
                    hitChance = 100;
            }
        }

        public Unit UnitToAttack
        {
            get { return unitToAttack; }

            set { unitToAttack = value; }
        }

        public Tile TileBeforeMoving
        {
            get { return tileBeforeMoving; }
        }

        public Tile Tile
        {
            get { return tile; }

            set
            {
                if (tile != null)
                {
                    tile.OccupiedBy = null;
                }
                tile = value;
                if (tile != null)
                {
                    tile.OccupiedBy = this;
                }
            }
        }

        public List<Tile> Path
        {
            get { return path; }

            set { path = value; }
        }

        public Player Player
        {
            get { return player; }

            set { player = value; }
        }
        #endregion Properties

        #region Methods
        public void Attack(Unit unit)
        {
            if (unit != null && unit.IsActive && InRange(unit))
            {
                if (random.Next(100) + 1 <= hitChance)
                    unit.Hit(GetUnitVsUnitDamage(unit));
                phase = Phase.Done;
            }
        }

        public abstract int GetUnitVsUnitDamage(Unit unit);

        #region Update
        public void Update()
        {
            if (IsActive)
            {
                if (path != null)
                {
                    moving = true;
                    if (tile == null) // SHOULD never happen
                        tile = path.First();

                    MoveBy(Var.MOVE_SPEED); // move the enemy (properly)

                    phase = Phase.Attack;
                    movementLeft = 0;

                    if (Center == path.Last().Center)
                    {
                        moving = false;
                        path = null;
                        if (unitToAttack != null)
                        {
                            UpdateAttackCall();
                        }
                        return;
                    }

                    // check attack stuff/etc here
                }
                else if (unitToAttack != null)
                {
                    UpdateAttackCall();
                }
            }
        }

        /// <summary> Called in Update to start Attack. </summary>
        private void UpdateAttackCall()
        {
            Attack(unitToAttack);
            unitToAttack.Attack(this);
            unitToAttack = null;
        }
        #endregion Update

        #region Move
        private void MoveBy(float num)
        {
            Tile nextTile = tile;

            int nextTileNum = path.IndexOf(tile) + 1;
            if (nextTileNum < path.Count)
            {
                nextTile = path[nextTileNum];
                // Move Left
                if (nextTile.CenterX < CenterX)
                {
                    // Encase Rounding a corner
                    if (CenterX - num < nextTile.CenterX)
                    {
                        Tile = nextTile;
                        // How far off it went over center
                        float distOff = num - (CenterX - tile.CenterX);
                        X -= num - distOff;
                        MoveBy(distOff);
                    }
                    // Otherwise move normally
                    else
                    {
                        Rotation = LEFT - ImageObject.NINETY;
                        X -= num;
                        if (CenterX == nextTile.CenterX)
                        {
                            Tile = nextTile;
                            MovementLeft -= Tile.Weight;
                        }
                    }
                }
                // Move Up
                else if (nextTile.CenterY < CenterY)
                {
                    // Encase Rounding a corner
                    if (CenterY - num < nextTile.CenterY)
                    {
                        Tile = nextTile;
                        // How far off it went over center
                        float distOff = num - (CenterY - tile.CenterY);
                        Y -= num - distOff;
                        MoveBy(distOff);
                    }
                    // Otherwise move normally
                    else
                    {
                        Rotation = UP - ImageObject.NINETY;
                        Y -= num;
                        if (CenterY == nextTile.CenterY)
                        {
                            Tile = nextTile;
                            MovementLeft -= Tile.Weight;
                        }
                    }
                }
                // Move Right
                else if (nextTile.CenterX > CenterX)
                {
                    // Encase Rounding a corner
                    if (CenterX + num > nextTile.CenterX)
                    {
                        Tile = nextTile;
                        // How far off it went over center
                        float distOff = num - (tile.CenterX - CenterX);
                        X += num - distOff;
                        MoveBy(distOff);
                    }
                    // Otherwise move normally
                    else
                    {
                        Rotation = RIGHT - ImageObject.NINETY;
                        X += num;
                        if (CenterX == nextTile.CenterX)
                        {
                            Tile = nextTile;
                            MovementLeft -= Tile.Weight;
                        }
                    }
                }
                // Move Down
                else if (nextTile.CenterY > CenterY)
                {
                    // Encase Rounding a corner
                    if (CenterY + num > nextTile.CenterY)
                    {
                        Tile = nextTile;
                        // How far off it went over center
                        float distOff = num - (tile.CenterY - CenterY);
                        Y += num - distOff;
                        MoveBy(distOff);
                    }
                    // Otherwise move normally
                    else
                    {
                        Rotation = DOWN - ImageObject.NINETY;
                        Y += num;
                        if (CenterY == nextTile.CenterY)
                        {
                            Tile = nextTile;
                            MovementLeft -= Tile.Weight;
                        }
                    }
                }
            }
        }
        #endregion Move

        public void Refresh()
        {
            movementLeft = maxMovement;
            phase = Phase.Move;
            tileBeforeMoving = tile;
            Center = tile.Center;
            CenterOrigin();
        }

        /// <summary> Returns if target is in range. </summary>
        public bool InRange(Unit unit)
        {
            if (unit != null && unit.Tile != null && tile != null)
            {
                if (Math.Abs(tile.Spot.X - unit.Tile.Spot.X) + Math.Abs(tile.Spot.Y - unit.Tile.Spot.Y) <= range)
                {
                    return true;
                }
            }
            return false;
        }

        public void Hit(int damage)
        {
            health -= damage;
            hit = 15;
            if (health <= 0)
            {
                isActive = false;
                tile.OccupiedBy = null;
            }
        }

        #endregion Methods

        #region Draw
        public override void Draw()
        {
            Color colour = color;
            if (hit != 0)
            {
                color = Color.Black;
                hit--;
            }
            else if (PhaseDone)
            {
                color = Color.Multiply(color, .40f);
                color.A = 255;
            }
            else
            {
                color = Color.White;
            }

            if (isActive)
                base.Draw();
            Color = colour;
        }
        #endregion Draw
    }
}
